#include <any>
#include <iostream>
#include <string>
#include <vector>

template <typename T>
void firstTest(T t);

class Variant
{
public:
    Variant() noexcept : d() {}
    ~Variant();
    Variant(const Variant& other);
    explicit Variant(std::any&& other) noexcept;
    explicit Variant(const std::any& other);
    explicit Variant(int i);
    explicit Variant(unsigned int ui);
    explicit Variant(long long ll);
    explicit Variant(unsigned long long ull);
    explicit Variant(bool b);
    explicit Variant(double d);
    explicit Variant(float f);
    explicit Variant(const char* str);
    explicit Variant(const std::string& str);

    [[nodiscard]] std::string ValueType() const;

    inline Variant(Variant&& other) noexcept : d(std::move(other.d)) {}

    Variant& operator=(const Variant& other) = default;

    inline Variant& operator=(Variant&& other) noexcept
    {
        std::swap(d, other.d);
        return *this;
    }

    inline void swap(Variant& other) noexcept { std::swap(d, other.d); }

    int toInt(bool* ok = nullptr) const;
    unsigned int toUInt(bool* ok = nullptr) const;
    long long toLongLong(bool* ok = nullptr) const;
    unsigned long long toULongLong(bool* ok = nullptr) const;
    [[nodiscard]] bool toBool() const;
    double toDouble(bool* ok = nullptr) const;
    float toFloat(bool* ok = nullptr) const;
    [[nodiscard]] std::string toString() const;

    template <typename T>
    inline void setValue(const T& value)
    {
        d = value;
    }

    template <class Type, class... Args>
    inline void emplaceValue(Args&&... args)
    {
        d = std::move(std::make_any<Type>(std::forward<Args>(args)...));
    }

    template <typename T>
    [[nodiscard]] inline T value() const
    {
        return std::any_cast<T>(d);
    }

    template <typename T>
    static inline Variant fromValue(const T& value)
    {
        return Variant(value);
    }

    template <class Type, class... Args>
    static inline Variant fromValue(Args&&... args)
    {
        return Variant(std::make_any<Type>(std::forward<Args>(args)...));
    }

private:
    std::any d;
};

class ObjA
{
public:
    explicit ObjA(const int value) : a(value) {}
    int a{111};
};

int main()
{
    // Variant var(1U);

    // auto value1 = var.toBool();
    // auto value2 = var.toInt();
    // auto value3 = var.toUInt();

    // std::cout << "value type:" << var.ValueType() << ",value:" << value1 << std::endl;
    // std::cout << "value type:" << var.ValueType() << ",value:" << value2 << std::endl;
    // std::cout << "value type:" << var.ValueType() << ",value:" << value3 << std::endl;

    // auto objValue = Variant::fromValue<ObjA>(222);
    // auto obj = objValue.value<ObjA>();
    // std::cout << "value type:" << objValue.ValueType() << ",value:" << obj.a << std::endl;

    // auto objValue = Variant::fromValue<ObjA>(ObjA(333));
    // auto obj = objValue.value<ObjA>();
    // std::cout << "value type:" << objValue.ValueType() << ",value:" << obj.a << std::endl;

    auto objValue = Variant();
    objValue.emplaceValue<ObjA>(444);
    const auto obj = objValue.value<ObjA>();
    std::cout << "value type:" << objValue.ValueType() << ",value:" << obj.a << std::endl;

    return 0;
}

template <typename T>
void firstTest(T t)
{
    std::any a;
    a = t;

    if (a.type() == typeid(std::string))
    {
        const auto var = std::any_cast<std::string>(a);
        std::cout << "Get string " << var << std::endl;
    }
    else if (a.type() == typeid(int))
    {
        const auto var = std::any_cast<int>(a);
        std::cout << "Get int " << var << std::endl;
    }
    else if (a.type() == typeid(float))
    {
        const auto var = std::any_cast<float>(a);
        std::cout << "Get float " << var << std::endl;
    }
    else if (a.type() == typeid(double))
    {
        const auto var = std::any_cast<double>(a);
        std::cout << "Get double " << var << std::endl;
    }
    else
    {
        std::cout << "Unknown value type." << std::endl;
    }
}

Variant::~Variant() { d.reset(); }

Variant::Variant(const Variant& other) { d = other.d; }

Variant::Variant(std::any&& other) noexcept { d.swap(other); }

Variant::Variant(const std::any& other) { d = other; }

Variant::Variant(int i) { d = i; }

Variant::Variant(unsigned int ui) { d = ui; }

Variant::Variant(long long ll) { d = ll; }

Variant::Variant(unsigned long long ull) { d = ull; }

Variant::Variant(bool b) { d = b; }

Variant::Variant(double d) { this->d = d; }

Variant::Variant(float f) { d = f; }

Variant::Variant(const char* str) { d = std::string(str); }

Variant::Variant(const std::string& str) { d = str; }

std::string Variant::ValueType() const
{
    if (d.has_value())
        return d.type().name();
    return "Unknown Type.";
}

int Variant::toInt(bool* ok) const
{
    if (d.type() == typeid(int))
    {
        if (ok)
            *ok = true;

        return std::any_cast<int>(d);
    }
    if (ok)
        *ok = false;
    return 0;
}

unsigned int Variant::toUInt(bool* ok) const
{
    if (d.type() == typeid(unsigned int))
    {
        if (ok)
            *ok = true;

        return std::any_cast<unsigned int>(d);
    }
    if (ok)
        *ok = false;
    return 0;
}

long long Variant::toLongLong(bool* ok) const
{
    if (d.type() == typeid(long long))
    {
        if (ok)
            *ok = true;

        return std::any_cast<long long>(d);
    }
    if (ok)
        *ok = false;
    return 0;
}

unsigned long long Variant::toULongLong(bool* ok) const
{
    if (d.type() == typeid(unsigned long long))
    {
        if (ok)
            *ok = true;

        return std::any_cast<unsigned long long>(d);
    }
    if (ok)
        *ok = false;
    return 0;
}

bool Variant::toBool() const
{
    if (d.type() == typeid(bool))
    {
        return std::any_cast<bool>(d);
    }
    return false;
}

double Variant::toDouble(bool* ok) const
{
    if (d.type() == typeid(double))
    {
        if (ok)
            *ok = true;

        return std::any_cast<double>(d);
    }
    if (ok)
        *ok = false;
    return 0;
}

float Variant::toFloat(bool* ok) const
{
    if (d.type() == typeid(float))
    {
        if (ok)
            *ok = true;

        return std::any_cast<float>(d);
    }
    if (ok)
        *ok = false;
    return 0;
}

std::string Variant::toString() const
{
    if (d.type() == typeid(std::string))
    {
        return std::any_cast<std::string>(d);
    }
    return {};
}
